﻿using System;
using System.Linq;
using MartinRL.ProjectEuler.Sequences;

namespace MartinRL.ProjectEuler
{
    /// <summary>
    /// A Pythagorean triplet is a set of three natural numbers, a  b  c, for which, a2 + b2 = c2
    /// For example, 32 + 42 = 9 + 16 = 25 = 52.
    ///
    /// There exists exactly one Pythagorean triplet for which a + b + c = 1000.
    /// Find the product abc.
    /// 
    /// http://projecteuler.net/index.php?section=problems&id=9
    /// </summary>
    public static class Problem009
    {
        public static int FindTheProductAbc()
        {
            const int sum = 1000;
            var numberSequence = 0.To(sum / 2);

            return numberSequence
                .SelectMany(a => numberSequence
                    .SelectMany(b => numberSequence
                        .Select(c => new {a, b, c})))
                .AsParallel()
                .Where(x => PythagoreanTriplet.Invariant(x.a, x.b, x.c))
                .Select(x => new PythagoreanTriplet(x.a, x.b, x.c))
                .Single(pt => pt.Sum == sum)
                .Product;
        }
    }

    public struct PythagoreanTriplet
    {
        public static bool Invariant(int a, int b, int c)
        {
            return (a < b && b < c) && ((a.Sqr() + b.Sqr()) == c.Sqr());
        }

        public readonly int A, B, C;

        public PythagoreanTriplet(int a, int b, int c)
        {
            if (!Invariant(a, b, c))
                throw new ArgumentException(
                    "The arguments must fulfill \"a < b < c\" and \"a^2 + b^2 = c^2\", but was a: {0}, b: {1} and c: {2}."
                        .FormatWith(a, b, c));

            A = a; B = b; C = c;
        }

        public int Sum { get { return A + B + C; } }

        public int Product { get { return A * B * C; } }

        public override string ToString()
        {
            return "A: {0}, B: {1}, C: {2}".FormatWith(A, B, C);
        }
    }
}